package sr;

import java.util.*;

import antlr.collections.AST;

import ptolemy.actor.*;
import ptolemy.graph.*;
import ptolemy.kernel.*;
import ptolemy.kernel.util.*;

import MoC.*;
import gui.*;
import db.*;
import FlowGraph.*;
import FlowGraph.Nodes.*;
import MethodGraph.*;
import SymbolTable.*;
import sdf.PortSampleRate;
import util.*;
import FlowGraph.StatementInfo.*;
import FlowGraph.SSA.*;
import JavaGrammar.*;
import simplification.*;

//////////////////////////////////////////////////////////////////////////
//// SRVerifier
/**

    * AtomicActorFor SR : pode colocar o composite em uma topologia SR que
    esta valido. (SRA, ou SDF quando for schedulable, valido como atomico
    e homo)

@author Ivan Jeukens
@version v 0.1 30/01/2002
*/
public class SRVerifier implements MoCVerifier {

    public SRVerifier() {
        _resultMessages = new ResultMessages("SR Validation");
    }

    ///////////////////////////////////////////////////////////////////
    ////                        public methods                     ////

    public String getName() {
        return "SR";
    }

    public void analyze(CompositeActorInfo info, ProgressDialog progress) {
        CompositeActorResult compRes = info.getResult();
        if(compRes.isSpecific() && !compRes.getSpecificMoC().equals("SR")) {
            return;
        }

        boolean invalid = false;
        Iterator ite = info.atomicActorsIterator();
        while(ite.hasNext()) {
            AtomicActorInfo ainfo = (AtomicActorInfo) ite.next();
            ClassResult cres = ainfo.getResult();
            if(!cres.isValidFor("SR")) {
                invalid = true;
                break;
            }
        }
        if(invalid) return;

        ite = info.compositeActorsIterator();
        while(ite.hasNext()) {
            CompositeActorInfo subinfo = (CompositeActorInfo) ite.next();
            CompositeActorResult res = subinfo.getResult();
            if(!res.isValidAsAtomicActorFor("SR")) {
                invalid = true;
                break;
            }
        }
        if(invalid) return;

         // Todos os atores contidos sao validos para SDF. Entao, verifica 
        // a topologia.
        invalid = _checkTopology(info);
        if(!invalid) {
            compRes.addAssociatedDirector("SR");

            StringBuffer buf = new StringBuffer(info.getName() + 
                " valid as atomic for: SR");
            compRes.setValidAsAtomicActorFor("SR");
            if(_isHomogeneous(info)) {
                compRes.setSRHomogeneous();
                compRes.setValidAsAtomicActorFor("SDF");
                buf.append(" SDF");
            }
	        SRValidationMessage m = new SRValidationMessage(info.getName(), 
                Message.INFORMATION, Message.VALIDASATOMIC, buf.toString());
            ActorInstanceMessageLocation loc = new ActorInstanceMessageLocation(
                MessageLocation.ACTOR_INSTANCE);
            loc.setActor(info.getEntity());
            m.setLocation(loc);
            _resultMessages.addMessage(m);
        }
        else {
            SRValidationMessage m = new SRValidationMessage(info.getName(), 
                Message.INFORMATION, Message.VALIDASATOMIC, 
                "Topology " + info.getName() + " invalid");
            _resultMessages.addMessage(m);
        }
    }

    public void analyze(ClassInfo cinfo, ProgressDialog progress) {
        ClassResult cres = cinfo.getResult();
        if(cres.isSpecific() && !cres.getSpecificMoC().equals("SR")) return;
        if(!cres.toVerify("SR")) return;

        cres.removeToVerify("SR");
        hasError = false;

        progress.addMessage("Performing SR analysis on " + cinfo.getName(), 1);

        boolean invalid = _checkPostfire(cinfo);
        if(invalid) return;

        invalid = _findSampleRates(cinfo);
        if(invalid) return;

        _findOutputPortDependencies(cinfo);

        if(!hasError) {  // feio, mas to com pressa
            cres.setValidFor("SR");
        }
    }

    public ResultMessages getResult() {
        return _resultMessages;
    }

    ///////////////////////////////////////////////////////////////////
    ////                        private methods                    ////

    // *************** TOPOLOGIA *************************************

    private boolean _checkTopology(CompositeActorInfo cinfo) {
        CompositeEntity top = cinfo.getEntity();

        //LinkedList solutions = _createSolutions(cinfo);
        //if(solutions == null) return false;

        DirectedGraph graph = new DirectedGraph();
        HashMap portmap = new HashMap();
        HashMap nodemap = new HashMap();

        List elist = top.entityList();
        Iterator ite = elist.iterator();
        while(ite.hasNext()) {
            ComponentEntity ent = (ComponentEntity) ite.next();
            Iterator pite = ent.portList().iterator();
            while(pite.hasNext()) {
                IOPort p = (IOPort) pite.next();
                Node n = graph.addNode();
                portmap.put(p, n);
                nodemap.put(n, p);
            }
        }
        // Poe os portos de entrada/saida da topologia
        Iterator pite = top.portList().iterator();
        while(pite.hasNext()) {
            IOPort p = (IOPort) pite.next();
            Node n = graph.addNode();
            portmap.put(p, n);
            nodemap.put(n, p);
        }

        elist = top.entityList();
        ite = elist.iterator();
        while(ite.hasNext()) {
            ComponentEntity ent = (ComponentEntity) ite.next();
            if(ent.isAtomic()) {
                // Conexoes internas
                AtomicActorInfo ainfo = cinfo.getAtomicActorInfo(
                    ent.getFullName());

//                System.out.println("Construindo conexoes internas para : " + 
//                    ainfo.getName());

                ClassResult res = ainfo.getResult();
                //System.out.println("Res = " + res);
                HashMap outputDep = res.getSROutputDependency();
                //System.out.println("Outdep = " + outputDep);

                Iterator oudite = outputDep.values().iterator();
                while(oudite.hasNext()) {
                    OutputPortDependency outd = (OutputPortDependency) oudite.next();

                    IOPort outp = (IOPort) ent.getPort(outd.getName());
                    Node outn = (Node) portmap.get(outp);
                    Iterator depite = outd.dependencyIterator();
                    while(depite.hasNext()) {
                        String pn = (String) depite.next();
                        IOPort inp = (IOPort) ent.getPort(pn);
                        Node inn = (Node) portmap.get(inp);
                        graph.addEdge(inn, outn);
//                        System.out.println("Edge " + pn + " -> " + outd.getName());
                    }
                }
            }
            else {  // Composite
                CompositeActorInfo containedcinfo = cinfo.getCompositeActorInfo(
                    ent.getFullName());
                CompositeActorResult res = containedcinfo.getResult();

                if(res.isValidAsAtomicActorFor("SR")) {
                    SRDepGraph depgraph = res.getSRDepGraph();
                    DirectedGraph dgraph = depgraph.getGraph();
                    HashMap depportmap = depgraph.getPortMap();

                    CompositeActor ca = (CompositeActor) ent;
                    Iterator inpite = ca.inputPortList().iterator();
                    while(inpite.hasNext()) {
                        IOPort inp = (IOPort) inpite.next();
                        Node inn = (Node) portmap.get(inp);
                        Node depinn = (Node) depportmap.get(inp);
                        Collection col = dgraph.reachableNodes(depinn);

                        Iterator outpite = ca.outputPortList().iterator();
                        while(outpite.hasNext()) {
                            IOPort outp = (IOPort) outpite.next();
                            Node outn = (Node) portmap.get(outp);
                            Node depoutn = (Node) depportmap.get(outp);

                            if(col.contains(depoutn)) {
                                graph.addEdge(inn, outn);
                            }
                        }
                    }
                }
                else {
                    CompositeActor ca = (CompositeActor) ent;
    //                System.out.println("Construindo conexoes internas para composite: " + 
    //                    ainfo.getName());
                    Iterator inpite = ca.inputPortList().iterator();
                    while(inpite.hasNext()) {
                        IOPort inp = (IOPort) inpite.next();
                        Node inn = (Node) portmap.get(inp);
                        Iterator outpite = ca.outputPortList().iterator();
                        while(outpite.hasNext()) {
                            IOPort outp = (IOPort) outpite.next();
                            Node outn = (Node) portmap.get(outp);
                            graph.addEdge(inn, outn);
    //			System.out.println("Edge " + inp.getName() + " -> " + outp.getName());
                        }
                    }
                }
            }

            // Conexoes externas
            pite = ent.portList().iterator();
            while(pite.hasNext()) {
                IOPort p = (IOPort) pite.next();
                if(p.isOutput()) {
                    Node outn = (Node) portmap.get(p);
                    Iterator conp = p.connectedPortList().iterator();
                    while(conp.hasNext()) {
                        Port inp = (Port)  conp.next();
                        Node inn = (Node) portmap.get(inp);
//                        System.out.println("Edge " + p.getFullName() + " -> " + 
//                             inp.getFullName());
                        graph.addEdge(outn, inn);

                    }
                }
            }
        }

        if(graph.isAcyclic()) {
            CompositeActorResult cres = cinfo.getResult();
            SRDepGraph depgraph = new SRDepGraph(graph, portmap, nodemap);
            cres.setSRDepGraph(depgraph);

            return false;
        }
        else {
            String sum = new String("Not SR schedulable, " +
                "there are cycles.");
            StringBuffer buf = new StringBuffer("Cycle contains:\n");
            ite = graph.cycleNodeCollection().iterator();
            while(ite.hasNext()) {
                Node n = (Node) ite.next();
                IOPort p = (IOPort) nodemap.get(n);
                buf.append("\t " + p.getFullName() + "\n");
            }
            SRValidationMessage m = new SRValidationMessage(cinfo.getName(), 
                Message.ERROR, Message.NOTSRSCHEDULABLE, sum);
            m.setText(buf.toString());
            _resultMessages.addMessage(m);
        }

        return true;
    }

    private boolean _isHomogeneous(CompositeActorInfo subinfo) {
        boolean sdfinvalid = false;
        CompositeEntity ce = subinfo.getEntity();
        Iterator pite = ce.portList().iterator();

        while(pite.hasNext()) {
            IOPort p = (IOPort) pite.next();

            List l = p.insideSinkPortList();
            Iterator ite = l.iterator();
            while(ite.hasNext()) {
                IOPort cp = (IOPort) ite.next();
                ComponentEntity e = (ComponentEntity) cp.getContainer();
                if(e.isAtomic()) {
                    AtomicActorInfo ainfo = subinfo.getAtomicActorInfo(e.getFullName());
                    ClassResult res = ainfo.getResult();

                    PortSampleRate psr = res.getSRPortSampleRate(cp.getName());
                    if(!psr.isConsumptionHomogeneous()) {
                        return false;
                    }
                }
                else {
                    CompositeActorInfo cinfo = subinfo.getCompositeActorInfo(e.getFullName());
                    CompositeActorResult res = cinfo.getResult();

                    if(!res.isSRHomogeneous()) return false;
                }
            }

            l = p.insideSourcePortList();
            ite = l.iterator();
            while(ite.hasNext()) {
                IOPort cp = (IOPort) ite.next();
                ComponentEntity e = (ComponentEntity) cp.getContainer();
                if(e.isAtomic()) {
                    AtomicActorInfo ainfo = subinfo.getAtomicActorInfo(e.getFullName());
                    ClassResult res = ainfo.getResult();

                    PortSampleRate psr = res.getSRPortSampleRate(cp.getName());
                    if(!psr.isProductionHomogeneous()) {
                        return false;
                    }
                }
                else {
                    CompositeActorInfo cinfo = subinfo.getCompositeActorInfo(e.getFullName());
                    CompositeActorResult res = cinfo.getResult();

                    if(!res.isSRHomogeneous()) return false;
                }
            }
        }
        return true;
    }

    // *************** CAUSALIDADE ************************************

    private void _findOutputPortDependencies(ClassInfo actor) {
        // inicializa, descobrindo todos os portos de saida, e a quantidade
        // de portos de entrada
        HashMap outputDep = new HashMap();

        ClassData root = actor.getRootClass();
        Iterator allports = root.getAllPorts();
        LinkedList inputports = new LinkedList();
        while(allports.hasNext()) {
            STPortEntry port = (STPortEntry) allports.next();
            if(port.isOutput()) {
                OutputPortDependency outpd = new OutputPortDependency(port);
                outputDep.put(outpd.getName(), outpd);
            }
            if(port.isInput()) {
                inputports.add(port.getName());
            }
        }

        allports = outputDep.values().iterator();
        while(allports.hasNext()) {
            OutputPortDependency outpd = (OutputPortDependency) allports.next();
            outpd.setTotalInputs(inputports.size());
        }

        // Procura as dependencias. Primerio pelo fire. Depois pelo prefire. Isso
        // para considerar o efeito de variaveis da classe.
        LinkedList ll = root.getMethod("fire");
        HashMap methodVariables = new HashMap();
        if(ll != null && ll.size() > 0) {
            STMethodEntry entry = (STMethodEntry) ll.get(0);
            FlowGraph firefg = entry.getFlowGraph();

            LinkedList depVarList = new LinkedList();
            _searchOutputDep(methodVariables, firefg, outputDep, root,
                inputports, actor, depVarList);
        }

        ll = root.getMethod("prefire");
        if(ll != null && ll.size() > 0) {
            STMethodEntry entry = (STMethodEntry) ll.get(0);
            FlowGraph prefirefg = entry.getFlowGraph();

            LinkedList depVarList = new LinkedList();
            _searchOutputDep(methodVariables, prefirefg, outputDep, root, 
                inputports, actor, depVarList);
        }

        SRValidationMessage m = new SRValidationMessage(actor.getName(), 
            Message.INFORMATION, Message.PORTSDEPENDENCIES, 
            new String(actor.getName() + " dependencies."));
        StringBuffer buf = new StringBuffer();
        Iterator ite = outputDep.values().iterator();
        while(ite.hasNext()) {
            OutputPortDependency outpd = (OutputPortDependency) ite.next();
            buf.append(outpd + "\n");
        }
        if(buf.length() == 0) {
            buf.append("No output ports for this actor.");
        }
        m.setText(buf.toString());
        _resultMessages.addMessage(m);

        ClassResult res = actor.getResult();
        res.setSROutputDependency(outputDep);
    }

    private void _searchOutputDep(HashMap methodVariables, FlowGraph fg,
            HashMap outputdep, ClassData cinfo, LinkedList inputports, 
            ClassInfo actor, LinkedList depVarList) {

        MethodGraph mg = fg.getMethodGraph();
                
        // Para todas essas variaveis, busca dependencias a partir do
        // statement de sua ultima definicao.
        //System.out.println("Processando variaveis de metodos");
        Iterator ite = methodVariables.values().iterator();
        HashMap newMethodVariables = new HashMap();
        while(ite.hasNext()) {
            MethodVariableDep mvdep = (MethodVariableDep) ite.next();
            if(mvdep.port.dependsAll()) continue;
            //System.out.println("Var " + mvdep.entry);

            SSA ssa = fg.getSSA();
            VariableInfo info = ssa.getVarInfo(mvdep.entry);
            LinkedList ll = info.getDefFromChain(info.defChainSize() - 1);

            if(ll.size() > 0) {
                HashMap visitedlocs = new HashMap();

                depVarList.add(info.toString());
                _findDataDependencies(newMethodVariables, ll, visitedlocs,
                    mvdep.port, ssa, cinfo, actor, outputdep, inputports, depVarList);
                depVarList.removeLast();

                if(mvdep.port.dependsAll() &&
                   (mvdep.port.dependencySize() < inputports.size())) {
                    for(int k = 0;k < inputports.size();k++) {
                        mvdep.port.addDependency((String)inputports.get(k), new LinkedList());
                    }
                }
            }
            else {
                newMethodVariables.put(mvdep.getKey(), mvdep);
            }
            //System.out.println("Depois de procurar nas variaveis\n" + mvdep.port);
        }
        methodVariables.clear();
        methodVariables.putAll(newMethodVariables);

        //System.out.println("Processando chamadas de metodos de producao");
        // Para todos os metodos de producao de dados.
        ite = mg.nodesIterator();
        while(ite.hasNext()) {
            MethodGraphNode mgnode = (MethodGraphNode) ite.next();
            if(mgnode.getType() == MethodGraphNode.SEND ||
               mgnode.getType() == MethodGraphNode.SENDARRAY ||
               mgnode.getType() == MethodGraphNode.BROADCAST ||
               mgnode.getType() == MethodGraphNode.BROADCASTARRAY) {

                String pname = mgnode.getPort();
                OutputPortDependency dep =
                    (OutputPortDependency) outputdep.get(pname);
                if(dep.dependsAll()) continue;

                depVarList.add(pname);
                if(_DEBUG) {
                    System.out.println("Encontrando dependencias para o porto " + pname);
                }

                MethodCallNode mcnode = mgnode.getMethodCallNode();
                StatementInfo info = mcnode.getStatementInfo();

                if(_DEBUG) {
                    System.out.println("DATA DEP");
                }
                
                // Procura as dependencia devido ao dado enviado
                // Um parametro eh constante, outro e a variavel do token
                Iterator uite = info.useIndexedVarIterator();
                while(uite.hasNext()) {
                    IndexedVarInfo ivi = (IndexedVarInfo) uite.next();
                    if(ivi.varinfo.toString().equals(pname)) continue;

                    if(_DEBUG) {
                    	System.out.println("def " + ivi.varinfo.toString() + "_" +
                    	    ivi.index);
                    }
                    LinkedList ll = ivi.varinfo.getDefFromChain(ivi.index);
                    if(!_useOfMethodVar(ivi, methodVariables, dep)) {
                        HashMap visitedlocs = new HashMap();
                        _findDataDependencies(methodVariables, ll, visitedlocs, dep,
                            fg.getSSA(), cinfo, actor, outputdep, inputports,
                            depVarList);
                    }

                    if(dep.dependsAll() &&
                       (dep.dependencySize() < inputports.size())) {
                        for(int k = 0;k < inputports.size();k++) {
                            dep.addDependency((String)inputports.get(k),
                                new LinkedList());
                        }
                    }
                }
                //System.out.println("Data dependency.\n" + dep);

                // Procura as dependencias devido ao caminho de controle,
                // e verifica que nao existe nenhum !isKnown() no meio do caminho
                if(!dep.dependsAll()) {
                    HashMap visitedlocs = new HashMap();

                    if(_DEBUG) {
                    	System.out.println("CONTROL DEP");
                    }
                    _findControlDependencies(visitedlocs, methodVariables, 
                        mcnode, dep, fg.getSSA(), cinfo, actor, outputdep, 
                        inputports, depVarList);
                }
                depVarList.removeLast();
                //System.out.println("After Control dependency.\n" + dep);
            }
            else
            if(mgnode.getType() == MethodGraphNode.SENDCLEAR ||
               mgnode.getType() == MethodGraphNode.BROADCASTCLEAR) {
                String pname = mgnode.getPort();
                OutputPortDependency dep =
                    (OutputPortDependency) outputdep.get(pname);
                if(dep.dependsAll()) continue;

                HashMap visitedlocs = new HashMap();
                depVarList.add(pname);

                if(_DEBUG) {
                    System.out.println("Buscando dependencias para o porto " + pname);
                    System.out.println("CONTROL DEP");
                }
                MethodCallNode mcnode = mgnode.getMethodCallNode();
                _findControlDependencies(visitedlocs, methodVariables, mcnode, dep, 
                    fg.getSSA(), cinfo, actor, outputdep, inputports, depVarList);

                depVarList.removeLast();
            }
            else
            if(mgnode.getType() == MethodGraphNode.GRAPH_LINK &&
                    mgnode.containsIOMethods()) {
                MethodGraph mgl = mgnode.getMethodGraph();
                FlowGraph fgl = mgl.getFlowGraph();

                _searchOutputDep(methodVariables, fgl, outputdep, cinfo,
                    inputports, actor, depVarList);
            }
        }
    }

    // A partir de um USE, descobre se existe alguma dependencia de sinais
    // de entrada
    // obs... no simplification, eu nao seto deleted dos nos dentro dos corpos,
    // so o beg e o end.
    private void _findDataDependencies(HashMap methodVariables,
            LinkedList defs, HashMap visitedlocs,
            OutputPortDependency dep, SSA ssa, ClassData root,
            ClassInfo actor, HashMap outputdep, LinkedList inputports,
            LinkedList depVarList) {

        if(defs == null) return;
        for(int i = 0;i < defs.size();i++) {
            VariableLocation loc = (VariableLocation) defs.get(i);
            FlowGraphNode fgnode = loc.getFGNode();

            if(fgnode.isDeleted()) continue;

            if(visitedlocs.containsKey(loc)) continue;
            visitedlocs.put(loc, null);

            if(_DEBUG) {
                System.out.println("DD: loc def = " + loc + "\n");
            }

            if(loc.isPhiNode()) {
                if(_DEBUG) {
                    System.out.println("DD: Phi node");
                }
                Iterator phiite = fgnode.phiNodeIterator();
                while(phiite.hasNext()) {
                    PhiNode phinode = (PhiNode) phiite.next();
                    if(phinode.getDefLabel() == loc.getASTNode()) {
                        VariableInfo vi = phinode.getVariableInfo();
                        for(int k = 0;k < phinode.inputSize();k++) {
                            AST var = phinode.getLabel(k);
                            if(Simplification.constantValue(var)) continue;

                            if(_DEBUG) {
                                System.out.println("Defs para " + vi.toString() + "_" +
                                    SSA.getVarIndex(var.getText()));    	
                            }
                            LinkedList l = vi.getDefFromChain(
                                SSA.getVarIndex(var.getText()));
                            
                            depVarList.add(vi.toString());
                            _findDataDependencies(methodVariables, l, visitedlocs, 
                                dep, ssa, root, actor, outputdep, inputports, depVarList);
                            depVarList.removeLast();
                            if(dep.dependsAll()) return;
                        }
                    }
                }
            }
            else {
                if(MethodGraph.isIOFGNode(fgnode)) {
                    MethodCallNode mcnode = (MethodCallNode) fgnode;
                    String mname = mcnode.getMethodName();
                    String caller = mcnode.getCallerName();
                    if(mname.equals("isKnown") && !outputdep.containsKey(caller)) {
                        StatementInfo info = mcnode.getStatementInfo();
                        Iterator ite = info.defIndexedVarIterator();
                        while(ite.hasNext()) {
                            IndexedVarInfo ivi = (IndexedVarInfo) ite.next();
                            LinkedList ll = ivi.varinfo.getUseFromChain(ivi.index);
                            if(ll != null) {
                                for(int k = 0;k < ll.size();k++) {
                                    VariableLocation ul = (VariableLocation) ll.get(k);
                                    if(!ul.isPhiNode()) {
                                        LinkedAST last = (LinkedAST) ul.getASTNode();
                                        last = (LinkedAST) last.getParent();
                                        if(last.getType() == JavaTokenTypes.LNOT) {
                                            LinkedList lines = new LinkedList();
                                            lines.add(mcnode.getLine());
                                            _newMessage(Message.ERROR, Message.NOTISKNOWN,
                                               "An output depends on the unknown stat of " +
                                               " an input." , lines, actor);
                                            hasError = true;
                                        }
                                        else
                                        if(last.getType() == JavaTokenTypes.EQUAL) {
                                            last = (LinkedAST) ul.getASTNode();
                                            last = (LinkedAST) last.getNextSibling();
                                            if(last.getText().equals("false")) {
                                                LinkedList lines = new LinkedList();
                                                lines.add(mcnode.getLine());
                                                _newMessage(Message.ERROR, Message.NOTISKNOWN,
                                                   "An output depends on the unknown stat of " +
                                                   " an input." , lines, actor);
                                                hasError = true;
                                            }
                                        }
                                        else
                                        if(last.getType() == JavaTokenTypes.NOT_EQUAL) {
                                            last = (LinkedAST) ul.getASTNode();
                                            last = (LinkedAST) last.getNextSibling();
                                            if(last.getText().equals("true")) {
                                                LinkedList lines = new LinkedList();
                                                lines.add(mcnode.getLine());
                                                _newMessage(Message.ERROR, Message.NOTISKNOWN,
                                                   "An output depends on the unknown stat of " +
                                                   " an input." , lines, actor);
                                                hasError = true;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    if(_DEBUG) {
                        System.out.println("DD: O Local do def eh um io_method");
                    }
                    dep.addDependency(mcnode.getCallerName(), depVarList);
                }
                else
                if(fgnode.getType() == FlowGraphNode.METHOD_CALL) {
                    MethodCallNode mcnode = (MethodCallNode) fgnode;
                    //if(mcnode.getFlowGraph() == null) {
                        // System.out.println("Procurando atraves de seus uses...");
                        StatementInfo info = mcnode.getStatementInfo();
                        if(_DEBUG) {
                            System.out.println("DD: analisando os use");
                        }
                        Iterator ite = info.useIndexedVarIterator();
                        while(ite.hasNext()) {
                            IndexedVarInfo uses = (IndexedVarInfo) ite.next();
                            //System.out.println("Chamando recursivamente para " +
                            //    uses.varinfo.toString());

                            if(!_useOfMethodVar(uses, methodVariables, dep)) {                                                        
                                LinkedList l = uses.varinfo.getDefFromChain(
                                    uses.index);

                                if(_DEBUG) {
                                    System.out.println("DD: var link " + 
                                        uses.varinfo.toString() + "_" +
                                        uses.index);
                                }

                                depVarList.add(uses.varinfo.toString());
                                _findDataDependencies(methodVariables, l, visitedlocs, 
                                    dep, ssa, root, actor, outputdep, inputports, depVarList);
                                depVarList.removeLast();
                            }
                            if(dep.dependsAll()) return;
                        }
                   // }
                    //else {
                       // FlowGraph mcfg = mcnode.getFlowGraph();
                        //MethodGraph mcmg = (MethodGraph) _methodGraphs.get(mcfg);

                        //_searchOutputDep(methodVariables, mcfg, mcmg, outputdep,
                        //    root, inputports, actor);

                        //dep.setDependsAll();
                    //}
                    if(_DEBUG) {
                    	System.out.println("DD: buscando dep de controle");
                    }
                    _findControlDependencies(visitedlocs, methodVariables, 
                        fgnode, dep, ssa, root, actor, outputdep, inputports, depVarList);
                }
                else {
                    StatementInfo info = loc.getStatementInfo();
                    //System.out.println("Procurando atraves de seus uses...");
                    //System.out.println("STATInfo = " + info);

                    if(info != null) {
                        if(_DEBUG) {
                            System.out.println("DD: analisando os use");
                        }
                        Iterator ite = info.useIndexedVarIterator();
                        while(ite.hasNext()) {
                            IndexedVarInfo uses = (IndexedVarInfo) ite.next();
                            //System.out.println("Chamando recursivamente para " +
                            //    uses.varinfo.toString());

                            if(!_useOfMethodVar(uses, methodVariables, dep)) {
                                LinkedList l = uses.varinfo.getDefFromChain(
                                    uses.index);
                                if(_DEBUG) {
                                    System.out.println("DD: var link " + 
                                    uses.varinfo.toString() + "_" + uses.index);
                                }
                                depVarList.add(uses.varinfo.toString());
                                _findDataDependencies(methodVariables, l, visitedlocs,
                                    dep, ssa, root, actor, outputdep, 
                                    inputports, depVarList);
                                depVarList.removeLast();
                            }
                            if(dep.dependsAll()) return;
                        }
                    }
                    else {
                        System.out.println("Find Data dep...O Stat info e null !!");
                    }
                    // para esse def, verifica a dependencia do caminho de controle
                    // dele.

                    if(_DEBUG) {
                        System.out.println("DD: buscando dep de controle");
                    }
                    _findControlDependencies(visitedlocs, methodVariables, fgnode, dep,
                        ssa, root, actor, outputdep, inputports, depVarList);
                }
            }
        }
    }

    // Eu tirei os dependsAll desse metodo, para forcar a verificacao da presenca
    // de um !isKnown()
    private void _findControlDependencies(HashMap visitedLocs, HashMap methodVariables,
            FlowGraphNode fgnode, OutputPortDependency dep, SSA ssa,
            ClassData root, ClassInfo actor, HashMap outputdep,
            LinkedList inputports, LinkedList depVarList) {
        if(fgnode.getScope() == 1) return;
        
        if(_DEBUG) {
            System.out.println("control dep de " + fgnode);
        }

        LinkedList process = new LinkedList();
        HashMap processed = new HashMap();
        for(int i = 0;i < fgnode.inputSize();i++) {
            FlowGraphEdge edge = fgnode.getInputEdge(i);
            if(edge.isBack() || edge.isDeleted()) continue;
            process.add(edge.getFrom());
        }

        while(process.size() > 0) {
            FlowGraphNode node = (FlowGraphNode) process.remove(0);
            if(processed.containsKey(node)) continue;
            processed.put(node, null);

            switch(node.getType()) {
                case FlowGraphNode.WHILE_BEGIN:
                case FlowGraphNode.SWITCH_BEGIN:
                case FlowGraphNode.IF_BEGIN: {
                    if(node.getScope() < fgnode.getScope()) {
                        StatementInfo info = node.getStatementInfo();
                        _searchControlDep(visitedLocs, methodVariables, 
                            info, dep, ssa, root, actor, outputdep, 
                            inputports, depVarList);
                        //if(dep.dependsAll()) return;
                    }
                } break;

                case FlowGraphNode.DO_BEGIN: {
                    if(node.getScope() < fgnode.getScope()) {
                        DoBeginNode beg = (DoBeginNode) node;
                        DoConditionNode cond = beg.getConditionNode();
                        StatementInfo info = cond.getStatementInfo();

                        _searchControlDep(visitedLocs, methodVariables, info,
                            dep, ssa, root, actor, outputdep, inputports, depVarList);
                        //if(dep.dependsAll()) return;
                    }
                } break;

/*                case FlowGraphNode.FOR_BEGIN: {
                    if(node.getScope() < fgnode.getScope()) {
                        ForBeginNode fbeg = (ForBeginNode) node;
                        StatementInfo info = fbeg.getInitInfo();

                        _searchControlDep(visitedLocs, methodVariables, info, 
                            dep, ssa, root, actor, outputdep, inputports, depVarList);
                        if(dep.dependsAll()) return;

                        info = fbeg.getConditionInfo();
                        _searchControlDep(visitedLocs, methodVariables, info, 
                            dep, ssa, root, actor, outputdep, inputports, depVarList);
                        //if(dep.dependsAll()) return;

                        info = fbeg.getIteratorInfo();
                        _searchControlDep(visitedLocs, methodVariables, info, 
                            dep, ssa, root, actor, outputdep, inputports, depVarList);
                        //if(dep.dependsAll()) return;
                    }
                } break; */
            }

            if(node.getScope() > 1) {
                for(int i = 0;i < node.inputSize();i++)  {
                    FlowGraphEdge edge = node.getInputEdge(i);
                    if(edge.isBack() || edge.isDeleted()) continue;
                    FlowGraphNode inn = edge.getFrom();
                    process.add(inn);
                }
            }
        }
    }

    private void _searchControlDep(HashMap visitedlocs, HashMap methodVariables, 
            StatementInfo info, OutputPortDependency dep, SSA ssa, ClassData root, 
            ClassInfo actor, HashMap outputdep, LinkedList inputports,
            LinkedList depVarList) {
        
        if(_DEBUG) {
            System.out.println("CD : uses para " + info.toString());
        }
        
        Iterator useite = info.useIndexedVarIterator();
        while(useite.hasNext()) {
            IndexedVarInfo ivi = (IndexedVarInfo) useite.next();

            LinkedList ll = ivi.varinfo.getDefFromChain(ivi.index);
            if(!_useOfMethodVar(ivi, methodVariables, dep)) {
            	if(_DEBUG) {
                    System.out.println("CD : use " + ivi.varinfo.toString() +  "_" +
                        ivi.index);
            	}
            	depVarList.add(ivi.varinfo.toString());
                _findDataDependencies(methodVariables, ll, visitedlocs, dep,
                    ssa, root, actor, outputdep, inputports, depVarList);
                depVarList.removeLast();
            }
        }
    }

    // Verifica se esse use e uma variavel de classe, sem ter tido uma def
    // ainda (index==0). Se for, adiciona na lista de variaveis entre metodos
    private boolean _useOfMethodVar(IndexedVarInfo ivi, HashMap methodVariables,
            OutputPortDependency dep) {
        if(ivi.index == 0) {
            STEntry entry = ivi.varinfo.getSTEntry();
            if(entry instanceof STVariableEntry) {
                MethodVariableDep md = 
                        new MethodVariableDep();
                md.entry = (STVariableEntry) entry;
                md.port = dep;
                if(!methodVariables.containsKey(md.getKey())) {
                    methodVariables.put(md.getKey(), md);
                }
                return true;
            }
        }
        return false;
    }
    
    // *************** SAMPLE RATES **********************************
    
    private void _printSampleRates(ClassInfo cinfo) {
        String sum = new String("Sample Rates for " + cinfo.getName());
        SRValidationMessage m = new SRValidationMessage(cinfo.getName(), 
            Message.INFORMATION, Message.SAMPLERATE, sum);

        StringBuffer buf = new StringBuffer();
        ClassResult res = cinfo.getResult();
        Iterator psrite = res.SRportsSampleRatesIterator();
        while(psrite.hasNext()) {
            PortSampleRate psr = (PortSampleRate) psrite.next();
            buf.append("port " + psr.getName() + " (" + 
                psr.production.getMin(0) + "," + psr.production.getMax(0) +
                ") (" + psr.consumption.getMin(0) + "," + 
                psr.consumption.getMax(0) + ")\n"); 
        }
        m.setText(buf.toString());
        _resultMessages.addMessage(m);
    }

    private boolean _findSampleRates(ClassInfo actor) {
        ClassResult ares = actor.getResult();
        boolean invalid = false;
        
        HashMap portssize = new HashMap();
        HashMap psr = new HashMap();
        ClassData root = actor.getRootClass();
        
        Iterator allports = root.getAllPorts();
        while(allports.hasNext()) {
            STPortEntry port = (STPortEntry) allports.next();
            PortSampleRate ps = new PortSampleRate(port.getName());
            psr.put(ps.getName(), ps);
            portssize.put(ps.getName(), new Integer(0));
        }

        MethodGraph prefiremg = null;
        MethodGraph firemg = null;

        LinkedList ll = root.getMethod("prefire");
        if(ll != null && ll.size() > 0) {
            STMethodEntry entry = (STMethodEntry) ll.get(0);
            FlowGraph fg = entry.getFlowGraph();
            prefiremg = fg.getMethodGraph();
            _findPortsSize(portssize, prefiremg);
        }

        ll = root.getMethod("fire");
        if(ll != null && ll.size() > 0) {
            STMethodEntry entry = (STMethodEntry) ll.get(0);
            FlowGraph fg = entry.getFlowGraph();
            firemg = fg.getMethodGraph();
            _findPortsSize(portssize, firemg);
        }

        _setPortsSize(psr, portssize);
        _resetPortsSampleRates(psr);

        if(prefiremg != null) {
            MethodGraphNode source = prefiremg.getSource();
            MethodGraphNode sink = prefiremg.getSink();

            source.setSRPortsSampleRates(psr);
            _searchForComm(source, sink);
            _computeInputSampleRates(sink, false);

            psr = sink.getSRPortsSampleRates();
        }

        if(firemg != null) {
            MethodGraphNode source = firemg.getSource();
            MethodGraphNode sink = firemg.getSink();

            source.setSRPortsSampleRates(psr);
            _searchForComm(source, sink);
            _computeInputSampleRates(sink, false);

            psr = sink.getSRPortsSampleRates();
        }

        Iterator ite = psr.values().iterator();
        while(ite.hasNext()) {
            int value = -1;
            LinkedList lines = new LinkedList();
            PortSampleRate sr = (PortSampleRate) ite.next();

            for(int i = 0;i < sr.production.minSize();i++) {
                if(sr.production.getMin(i) > 1 ||
                   sr.production.getMax(i) > 1) {
                    _newMessage(Message.ERROR, Message.NONUNITARYSAMPLERATE,
                       "Port " + sr.getName() + " have production rate > 1.",
                       lines, actor);
                   invalid = true;
                }
            }

            for(int i = 0;i < sr.consumption.minSize();i++) {
                if(sr.consumption.getMin(i) > 1 ||
                   sr.consumption.getMax(i) > 1) {
                    _newMessage(Message.WARNING, Message.NONUNITARYSAMPLERATE,
                       "Port " + sr.getName() + " have consumption rate > 1." +
                       " This is unecessary!!", lines, actor);
                }
            }
        }
        if(!invalid) {
            ares.setSRPortsSampleRates(psr);
            _printSampleRates(actor);
        }

        return invalid;
    }

    private void _findPortsSize(HashMap portssize, MethodGraph mg) {
        LinkedList top = mg.getTopologicalOrder();
        for(int i = 0;i < top.size();i++) {
            MethodGraphNode mgnode = (MethodGraphNode) top.get(i);

            if(mgnode.getType() == MethodGraphNode.GRAPH_LINK) {
                MethodGraph link = mgnode.getMethodGraph();
                if(!mgnode.containsIOMethods()) continue;
                if( link.getFlowGraph() == mg.getFlowGraph() ) { // isso aqui na pode!
                    continue;
                }
                _findPortsSize(portssize, link);
                continue;
            }
            else
            if(mgnode.getType() == MethodGraphNode.SINK ||
               mgnode.getType() == MethodGraphNode.SOURCE ||
               mgnode.getType() == MethodGraphNode.LOOPHEAD) {
                continue;
            }

            String port = mgnode.getPort();
            int channel = mgnode.getChannel();
            if(channel != MethodGraphNode.ALLCHANNELS) {
                Integer v = (Integer) portssize.get(port);
                if(v.intValue() < channel) {
                    portssize.put(port, new Integer(channel));
                }
            }
        }
    }
    
    private void _setPortsSize(HashMap ports, HashMap portssize) {
        Iterator ite = ports.values().iterator();
        while(ite.hasNext()) {
            PortSampleRate psr = (PortSampleRate) ite.next();
            int size = ((Integer) portssize.get(psr.getName())).intValue();
            psr.setChannelSize(size + 1);
        }
    }

    private void _resetPortsSampleRates(HashMap ports) {
        Iterator ite = ports.values().iterator();
        while(ite.hasNext()) {
            PortSampleRate psr = (PortSampleRate) ite.next();
            psr.reset();
        }
    }
    
    private void _searchForComm(MethodGraphNode firstNode,
            MethodGraphNode stopNode) {
        HashMap processed = new HashMap();
        LinkedList process = new LinkedList();
        process.add(firstNode);
        while(process.size() > 0) {
            MethodGraphNode mgnode = (MethodGraphNode) process.remove(0);
            if(processed.containsKey(mgnode) ||
               !_inputsHaveSampleRates(mgnode)) continue;

            processed.put(mgnode, null);
            switch(mgnode.getType()) {
                case MethodGraphNode.GRAPH_LINK: {
                    _computeInputSampleRates(mgnode, false);  // nao inclue o backedge

                    if(mgnode.containsIOMethods()) {
                        MethodGraph mg = mgnode.getMethodGraph();
                        MethodGraphNode source = mg.getSource();
                        MethodGraphNode sink = mg.getSink();

                        _resetSRPortsSampleRates(mg);

                        source.setSRPortsSampleRates(mgnode.getSRPortsSampleRates());
                        _searchForComm(source, sink);
                        _computeInputSampleRates(sink, false);
                        mgnode.setSRPortsSampleRates(sink.getSRPortsSampleRates());
                    }

                    for(int j = 0;j < mgnode.outputSize();j++) {
                        MethodGraphNode outn = mgnode.getOutputNode(j);
                        if(outn == stopNode) continue;
                        process.add(outn);
                    }
                } break;

                case MethodGraphNode.LOOPHEAD: {
                    FlowGraphNode fgn = mgnode.getLoopHead();
                    if(fgn.getType() == FlowGraphNode.DO_BEGIN) {
                        _computeInputSampleRates(mgnode, false);
                        _updateSampleRates(mgnode);
                        
                        for(int j = 0;j < mgnode.outputSize();j++) {
                            MethodGraphEdge edge = mgnode.getOutputEdge(j);
                            if(edge.isBack()) continue;
                            MethodGraphNode outn = edge.getTo();
                            if(outn == stopNode) continue;
                            process.add(outn);
                        }
                    }
                    else {
                        _computeInputSampleRates(mgnode, false);  // nao inclue o backedge

                        if(mgnode.containsIOMethods()) {
                            Iterator ite = mgnode.firstLoopNodesIterator();
                            while(ite.hasNext()) {
                                MethodGraphNode fln = (MethodGraphNode) ite.next();
                                _searchForComm(fln, mgnode);
                            }

                            mgnode.setSRPortsSampleRates(null);
                            _computeInputSampleRates(mgnode, true);   // so backedge
                        }
                        Iterator ite = mgnode.continueLoopNodesIterator();
                        while(ite.hasNext()) {
                            MethodGraphNode cln = (MethodGraphNode) ite.next();
                            if(cln == stopNode) continue;
                            process.add(cln);
                        }
                    }
                } break;

                default: {
                    _computeInputSampleRates(mgnode, false);
                    _updateSampleRates(mgnode);

                    for(int j = 0;j < mgnode.outputSize();j++) {
                        MethodGraphEdge edge = mgnode.getOutputEdge(j);
                        if(edge.isBack()) continue;
                        MethodGraphNode outn = edge.getTo();
                        if(outn == stopNode) continue;
                        process.add(outn);
                    }
                }
            }
        }
    }

    private void _resetSRPortsSampleRates(MethodGraph mg) {
        Iterator ite = mg.nodesIterator();
        while(ite.hasNext()) {
            MethodGraphNode node = (MethodGraphNode) ite.next();
            node.setSRPortsSampleRates(null);
        }
    }
    
    private boolean _inputsHaveSampleRates(MethodGraphNode mgnode) {
        for(int j = 0;j < mgnode.inputSize();j++) {
            MethodGraphEdge edge = mgnode.getInputEdge(j);
            if(edge.isBack()) continue;
            MethodGraphNode inmgnode = edge.getFrom();

            HashMap ps = inmgnode.getSRPortsSampleRates();
            if(ps == null) return false;
        }
        return true;
    }

    private void _updateSampleRates(MethodGraphNode mgnode) {
        String port = mgnode.getPort();
        HashMap map = mgnode.getSRPortsSampleRates();
        PortSampleRate psr = (PortSampleRate) map.get(port);
        int channel = mgnode.getChannel();
        int ntokens = mgnode.getNTokens();
        switch(mgnode.getType()) {
            case MethodGraphNode.SENDCLEAR:
            case MethodGraphNode.SEND:
            case MethodGraphNode.SENDARRAY: {
                if(channel == MethodGraphNode.ALLCHANNELS) {
                    for(int j = 0;j < psr.production.minSize();j++) {
                        psr.production.incrMin(j, ntokens);
                        psr.production.incrMax(j, ntokens);
                    }
                }
                else {
                    psr.production.incrMin(channel, ntokens);
                    psr.production.incrMax(channel, ntokens);
                }
            } break;

            case MethodGraphNode.BROADCASTCLEAR:
            case MethodGraphNode.BROADCAST:
            case MethodGraphNode.BROADCASTARRAY: {
                for(int j = 0;j < psr.production.minSize();j++) {
                    psr.production.incrMin(j, ntokens);
                    psr.production.incrMax(j, ntokens);
                }
            } break;

            case MethodGraphNode.GET:
            case MethodGraphNode.GETARRAY: {
                if(channel == MethodGraphNode.ALLCHANNELS) {
                    for(int j = 0;j < psr.consumption.minSize();j++) {
                        psr.consumption.incrMin(j, ntokens);
                        psr.consumption.incrMax(j, ntokens);
                    }
                }
                else {
                    psr.consumption.incrMin(channel, ntokens);
                    psr.consumption.incrMax(channel, ntokens);
                }
            } break;

            case MethodGraphNode.GRAPH_LINK: {
                System.out.println("SR---TEM UM GRAPH LINK NO UPDATESAMPLE!!!");
            } break;
            
            default:
        }
    }

    private void _computeInputSampleRates(MethodGraphNode mgnode,
            boolean withBackEdge) {
        HashMap psr = null;
        for(int j = 0;j < mgnode.inputSize();j++) {
            MethodGraphEdge edge = mgnode.getInputEdge(j);
            if( (withBackEdge && !edge.isBack()) ||
                (!withBackEdge && edge.isBack()) ) continue;

            MethodGraphNode inmgnode = edge.getFrom();
            HashMap inpsr = inmgnode.getSRPortsSampleRates();

            if(mgnode.getSRPortsSampleRates() == null) {
                mgnode.setSRPortsSampleRates(_copySampleRates(inpsr));
                continue;
            }
            else {
                psr = mgnode.getSRPortsSampleRates();
            }
            HashMap ps = inmgnode.getSRPortsSampleRates();

            Iterator ite = psr.values().iterator();
            while(ite.hasNext()) {
                PortSampleRate s = (PortSampleRate) ite.next();

                PortSampleRate ins = (PortSampleRate) ps.get(s.getName());
                s.updateInit(ins);
                s.updateProduction(ins);
                s.updateConsumption(ins);
            }
        }
    }

    private HashMap _copySampleRates(HashMap sr) {
        HashMap ret = new HashMap();
        Iterator ite = sr.values().iterator();
        while(ite.hasNext()) {
            PortSampleRate psr = (PortSampleRate) ite.next();
            PortSampleRate clone = (PortSampleRate) psr.clone();
            ret.put(clone.getName(), clone);
        }
        return ret;
    }

    // *********************** VERIFICA O POSTFIRE *************************
    
    private boolean _checkPostfire(ClassInfo cinfo) {
        boolean invalid = false;
        ClassData root = cinfo.getRootClass();

        LinkedList ll = root.getMethod("postfire");
        if(ll != null && ll.size() > 0) {
            STMethodEntry entry = (STMethodEntry) ll.get(0);
            FlowGraph fg = entry.getFlowGraph();
            MethodGraph mg = fg.getMethodGraph();

            LinkedList top = mg.getTopologicalOrder();
            for(int i = 0;i < top.size();i++) {
                MethodGraphNode mgnode = (MethodGraphNode) top.get(i);

                if(mgnode.getType() == MethodGraphNode.SEND ||
                    mgnode.getType() == MethodGraphNode.SENDARRAY ||
                    mgnode.getType() == MethodGraphNode.BROADCAST ||
                    mgnode.getType() == MethodGraphNode.SENDCLEAR ||
                    mgnode.getType() == MethodGraphNode.BROADCASTCLEAR) {
                        
                   MethodCallNode mcnode = mgnode.getMethodCallNode();
                   LinkedList lines = new LinkedList();
                   lines.add(mcnode.getLine());
                   _newMessage(Message.ERROR, Message.PRODINPOSTFIRE,
                       "The state of an output cannot be changed after " +
                       "convergence!", lines, cinfo);
                   invalid = true;
                }
            }
        }
        return invalid;
    }
    
    private Message _newMessage(int status, int code, String text,
            LinkedList lines, ClassInfo cinfo) {
        SRValidationMessage m = new SRValidationMessage(cinfo.getName(), status,
            code, text);
        ClassMessageLocation er = new ClassMessageLocation(
            MessageLocation.CLASS_SOURCE);
        for(int i = 0;i < lines.size();i++) {
            er.addLine((Line)lines.get(i));
        }
        er.setClassInfo(cinfo);
        m.setLocation(er);
        _resultMessages.addMessage(m);
        
        return m;
    }

    ///////////////////////////////////////////////////////////////////
    ////                        inner class                       ////

    // Para uma variavel de classe, qual e a dependencia dela. Usando
    // no processamento entre o fire e o prefire.
    class MethodVariableDep {
        public STVariableEntry entry;
        public OutputPortDependency port;

        public String getKey() {
            return new String(entry.getName() + "_" + port.getName());
        }
    }

    ///////////////////////////////////////////////////////////////////
    ////                        private variables                  ////

    private ResultMessages _resultMessages;
    
    private boolean _DEBUG = false;
    private boolean hasError;
}
